<!-- ER图显示组件 -->
<template>
  <el-card class="diagram-output" :body-style="{ padding: '0' }">
    <template #header>
      <div class="card-header">
        <div class="header-left">
          <i class="el-icon-connection" />
          <span>ER 图预览</span>
        </div>
        <el-dropdown @command="handleExportMenu">
          <el-button type="success" :icon="Download">
            导出图片<el-icon><arrow-down /></el-icon>
          </el-button>
          <template #dropdown>
            <el-dropdown-menu>
              <template v-for="group in exportOptions" :key="group.group">
                <el-dropdown-item disabled>
                  <b>{{ group.group }}</b>
                </el-dropdown-item>
                <el-dropdown-item
                  v-for="option in group.options"
                  :key="option.label + group.group"
                  :command="option"
                >
                  {{ option.label }}
                </el-dropdown-item>
                <el-dropdown-item divided v-if="group !== exportOptions[exportOptions.length - 1]" />
              </template>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
      </div>
    </template>
    <div class="diagram-wrapper">
      <div ref="diagramDiv" class="diagram-container" />
    </div>
    <ExportDialog
      v-model="isExportDialogVisible"
      @confirm="handleExportConfirm"
    />
    <!-- 保存/另存为弹窗 -->
    <el-dialog v-model="showSaveDialog" :title="saveType === 'saveAs' ? '另存为' : '保存'" width="400px" :close-on-click-modal="false">
      <el-form :model="saveForm">
        <el-form-item label="名称" prop="name">
          <el-input v-model="saveForm.name" placeholder="请输入名称" />
        </el-form-item>
        <el-form-item label="备注" prop="remark">
          <el-input v-model="saveForm.remark" placeholder="请输入备注" />
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="handleSaveCancel">取消</el-button>
        <el-button type="primary" @click="handleSaveConfirm">确认</el-button>
      </template>
    </el-dialog>
  </el-card>
</template>

<script>
import { ref, onMounted, nextTick, watch } from "vue";
import * as go from "gojs";
import { RadialLayout } from "@/utils/RadialLayout";
import ExportDialog from "./ExportDialog.vue";
import { ElMessage } from "element-plus";
import { ArrowDown } from "@element-plus/icons-vue";

export default {
  name: "ErDiagram",
  components: { ExportDialog, ArrowDown },
  emits: ["export"],
  setup(props, { emit }) {
    const diagramDiv = ref(null);
    let myDiagram = null;
    const isExportDialogVisible = ref(false);
    // 保存/另存为弹窗相关
    const showSaveDialog = ref(false);
    const saveForm = ref({ name: '', remark: '' });
    const saveType = ref('save'); // 'save' or 'saveAs'
    const handleSave = (type = 'save') => {
      saveType.value = type;
      showSaveDialog.value = true;
    };
    const handleSaveConfirm = () => {
      // 占位：保存/另存为逻辑
      // saveForm.value.name, saveForm.value.remark, saveType.value
      showSaveDialog.value = false;
      ElMessage.success('保存成功（占位）');
    };
    const handleSaveCancel = () => {
      showSaveDialog.value = false;
    };
    // 隐藏属性节点
    const showAttributes = ref(true);
    const toggleAttributes = () => {
      showAttributes.value = !showAttributes.value;
      if (!myDiagram) return;
      myDiagram.startTransaction('toggleAttributes');
      myDiagram.nodes.each(node => {
        if (node.data.category === 'Attribute') {
          node.visible = showAttributes.value;
        }
      });
      myDiagram.commitTransaction('toggleAttributes');
    };
    // 导出下拉菜单
    const exportOptions = [
      {
        group: '完整画布',
        options: [
          { label: 'PNG(白色背景)', mode: 'full', format: 'png', background: 'white' },
          { label: 'PNG(透明背景)', mode: 'full', format: 'png', background: 'transparent' }
        ]
      },
      {
        group: '当前视口',
        options: [
          { label: 'PNG(白色背景)', mode: 'viewport', format: 'png', background: 'white' },
          { label: 'PNG(透明背景)', mode: 'viewport', format: 'png', background: 'transparent' }
        ]
      }
    ];
    const handleExportMenu = (option) => {
      if (!myDiagram) return;
      let imageData;
      if (option.mode === 'full') {
        imageData = myDiagram.makeImageData({
          scale: 1,
          background: option.background === 'transparent' ? null : option.background,
          type: 'image/png',
          maxSize: new go.Size(Infinity, Infinity)
        });
      } else {
        imageData = myDiagram.makeImageData({
          scale: 1,
          background: option.background === 'transparent' ? null : option.background,
          type: 'image/png'
        });
      }
      const link = document.createElement('a');
      link.download = `er-diagram-${option.mode}-${option.background}.png`;
      link.href = imageData;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    };

    onMounted(async () => {
      await nextTick();
      initDiagram();
    });

    const initDiagram = () => {
      const $ = go.GraphObject.make;

      myDiagram = $(go.Diagram, diagramDiv.value, {
        initialContentAlignment: go.Spot.Center,
        "undoManager.isEnabled": true,
        layout: $(go.ForceDirectedLayout, {
          defaultSpringLength: 50,
          defaultElectricalCharge: 100,
          maxIterations: 20000          // 算法运行更充分
        }),
        "toolManager.mouseWheelBehavior": go.ToolManager.WheelZoom,
        allowSelect: true,
        padding: 40,
        minScale: 0.25,
        maxScale: 2,
        "animationManager.isEnabled": true,
        "grid.visible": false
      });

      const onNodeClicked = (e, node) => {
        emit('node-selected', node.data);
      };

      // Entity
      myDiagram.nodeTemplateMap.add(
        "Entity",
        $(
          go.Node,
          "Auto",
          {
            selectionAdornmentTemplate: $(
              go.Adornment,
              "Auto",
              $(go.Shape, { fill: null, stroke: "blue", strokeWidth: 2 }),
              $(go.Placeholder)
            ),
            click: onNodeClicked
          },
          $(
            go.Shape,
            "Rectangle",
            {
              name: "SHAPE",
              fill: "white",
              stroke: "#000000",
              strokeWidth: 2,
              width: 120,
              height: 60
            },
            new go.Binding("stroke", "color").makeTwoWay()
          ),
          $(
            go.TextBlock,
            {
              margin: 8,
              font: "bold 16px 微软雅黑",
              stroke: "#000000",
              editable: true
            },
            new go.Binding("text", "name").makeTwoWay(),
            new go.Binding("textDecoration", "underline", v => v ? "underline" : "none").makeTwoWay()
          )
        )
      );

      // Attribute
      myDiagram.nodeTemplateMap.add(
        "Attribute",
        $(
          go.Node,
          "Auto",
          {
            minSize: new go.Size(60, 40),
            selectionAdornmentTemplate: $(
              go.Adornment,
              "Auto",
              $(go.Shape, { fill: null, stroke: "blue", strokeWidth: 2 }),
              $(go.Placeholder)
            ),
            click: onNodeClicked
          },
          $(
            go.Shape,
            "Ellipse",
            {
              name: "SHAPE",
              fill: "white",
              stroke: "#000000",
              strokeWidth: 2,
              margin: 4
            },
            new go.Binding("stroke", "color").makeTwoWay()
          ),
          $(
            go.TextBlock,
            {
              margin: 8,
              font: "14px 微软雅黑",
              stroke: "#333",
              editable: true
            },
            new go.Binding("text", "name").makeTwoWay(),
            new go.Binding("textDecoration", "isPrimaryKey", v =>
              v ? "underline" : "none"
            ),
            new go.Binding("textDecoration", "underline", v => v ? "underline" : "none").makeTwoWay()
          )
        )
      );

      // Relationship
      myDiagram.nodeTemplateMap.add(
        "Relationship",
        $(
          go.Node,
          "Auto",
          {
            selectionAdornmentTemplate: $(
              go.Adornment,
              "Auto",
              $(go.Shape, { fill: null, stroke: "blue", strokeWidth: 2 }),
              $(go.Placeholder)
            ),
            click: onNodeClicked
          },
          $(
            go.Shape,
            "Diamond",
            {
              name: "SHAPE",
              fill: "white",
              stroke: "#000000",
              strokeWidth: 2,
              width: 60,
              height: 60
            },
            new go.Binding("stroke", "color").makeTwoWay()
          ),
          $(
            go.TextBlock,
            {
              margin: 8,
              font: "14px 微软雅黑",
              stroke: "#333",
              editable: true
            },
            new go.Binding("text", "name").makeTwoWay(),
            new go.Binding("textDecoration", "underline", v => v ? "underline" : "none").makeTwoWay()
          )
        )
      );

      // Link
      myDiagram.linkTemplate = $(
        go.Link,
        {
          routing: go.Link.Normal,
          corner: 5,
          curve: go.Link.JumpOver,
          selectionAdornmentTemplate: $(
            go.Adornment,
            $(go.Shape, { isPanelMain: true, stroke: "blue", strokeWidth: 2 })
          )
        },
        $(go.Shape, { strokeWidth: 1.5, stroke: "#000000" }, new go.Binding("stroke", "color").makeTwoWay()),
        $(
          go.TextBlock,
          {
            segmentOffset: new go.Point(0, -10),
            font: "12px 微软雅黑",
            editable: true
          },
          new go.Binding("text", "text").makeTwoWay()
        )
      );
    };

    const updateDiagram = tables => {
      const nodeDataArray = [];
      const linkDataArray = [];
      const foreignKeyRelations = new Map();

      tables.forEach(table => {
        table.foreignKeys?.forEach(fk => {
          if (fk.referenceTable) {
            foreignKeyRelations.set(
              `${table.tableName}-${fk.referenceTable}`,
              fk.comment
            );
          }
        });
      });

      tables.forEach(table => {
        nodeDataArray.push({
          category: "Entity",
          key: table.tableName,
          name: removeQuotes(table.comment) || table.tableName
        });

        table.columns.forEach(col => {
          const attrKey = `${table.tableName}-${col.name}`;
          nodeDataArray.push({
            category: "Attribute",
            key: attrKey,
            name: removeQuotes(col.comment) || col.name,
            isPrimaryKey: table.primaryKeys.includes(col.name)
          });
          linkDataArray.push({
            from: table.tableName,
            to: attrKey
          });
        });

        table.foreignKeys?.forEach(fk => {
          const relationKey = `${table.tableName}-${fk.referenceTable}`;
          const comment = foreignKeyRelations.get(relationKey);
          if (fk.referenceTable && comment) {
            const relationshipKey = `${relationKey}-relationship`;
            nodeDataArray.push({
              category: "Relationship",
              key: relationshipKey,
              name: removeQuotes(comment) || "关系"
            });
            linkDataArray.push({
              from: table.tableName,
              to: relationshipKey,
              text: "N"
            });
            linkDataArray.push({
              from: relationshipKey,
              to: fk.referenceTable,
              text: "1"
            });
            foreignKeyRelations.delete(relationKey);
          }
        });
      });

      myDiagram.model = new go.GraphLinksModel(nodeDataArray, linkDataArray);
      myDiagram.layoutDiagram(true);
      myDiagram.zoomToFit();
      myDiagram.contentAlignment = go.Spot.Center;
    };

    const removeQuotes = str => {
      if (!str) return str;
      return str.replace(/^'|'$/g, "");
    };

    const openExportDialog = () => {
      isExportDialogVisible.value = true;
    };

    const handleExportConfirm = options => {
      if (!myDiagram.nodes.count) return;

      const originalBackground = myDiagram.div.style.background;
      myDiagram.div.style.background =
        options.background === "transparent" ? "transparent" : "white";

      const imageData = myDiagram.makeImageData({
        scale: 1,
        background: options.background === "transparent" ? null : "white",
        type: options.format === "jpeg" ? "image/jpeg" : "image/png"
      });

      const now = new Date();
      const timestamp = `${now.getFullYear()}${String(
        now.getMonth() + 1
      ).padStart(2, "0")}${String(now.getDate()).padStart(2, "0")}${String(
        now.getHours()
      ).padStart(2, "0")}${String(now.getMinutes()).padStart(
        2,
        "0"
      )}${String(now.getSeconds()).padStart(2, "0")}`;

      const link = document.createElement("a");
      link.download = `${timestamp}ER图.${options.format}`;
      link.href = imageData;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      myDiagram.div.style.background = originalBackground;
    };

    const undo = () => {
      if (!myDiagram) return;
      if (myDiagram.commandHandler.canUndo()) {
        myDiagram.commandHandler.undo();
      } else {
        ElMessage.info('没有可撤销的操作');
      }
    };
    const redo = () => {
      if (!myDiagram) return;
      if (myDiagram.commandHandler.canRedo()) {
        myDiagram.commandHandler.redo();
      } else {
        ElMessage.info('没有可前进的操作');
      }
    };
    const downloadImage = () => {
      if (!myDiagram) return;
      const imageData = myDiagram.makeImageData({
        scale: 1,
        background: "white",
        type: "image/png"
      });
      const link = document.createElement("a");
      link.download = "er-diagram.png";
      link.href = imageData;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
    };
    const fitToScreen = () => {
      if (myDiagram) {
        myDiagram.zoomToFit();
        myDiagram.contentAlignment = go.Spot.Center;
      }
    };
    const fullscreen = () => {
      if (diagramDiv.value?.requestFullscreen) {
        diagramDiv.value.requestFullscreen();
      }
    };

    // 节点/线条颜色
    const setNodeColor = (color) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('setNodeColor');
      myDiagram.model.nodeDataArray.forEach(node => {
        if (
          node.category === 'Entity' ||
          node.category === 'Attribute' ||
          node.category === 'Relationship'
        ) {
          myDiagram.model.setDataProperty(node, 'color', color);
        }
      });
      myDiagram.commitTransaction('setNodeColor');
    };
    const setLineColor = (color) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('setLineColor');
      myDiagram.model.linkDataArray.forEach(link => {
        myDiagram.model.setDataProperty(link, 'color', color);
      });
      myDiagram.commitTransaction('setLineColor');
    };
    // 布局切换
    const setLayout = (type) => {
      if (!myDiagram) return;
      let layout;
      if (type === 'classic') {
        layout = go.GraphObject.make(go.ForceDirectedLayout, {
          defaultSpringLength: 50,
          defaultElectricalCharge: 100,
          maxIterations: 1000          // 算法运行更充分
        });
      } else if (type === 'tree') {
        layout = go.GraphObject.make(go.TreeLayout, {
          angle: 90,
          layerSpacing: 80
        });
      }else if (type === 'star') {
        layout = go.GraphObject.make(RadialLayout, {
          layerThickness: 120
        });
      }
      myDiagram.layout = layout;
      myDiagram.layoutDiagram(true);
    };
    // 连线文字位置
    const setLinkLabelPosition = (type) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('setLinkLabelPosition');
      myDiagram.links.each(link => {
        const label = link.findObject('TEXT');
        if (label) {
          label.segmentOffset = type === 'center' ? new go.Point(0, 0) : new go.Point(0, -10);
        }
      });
      myDiagram.commitTransaction('setLinkLabelPosition');
    };
    // 新建节点
    const addNode = (type, position) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('addNode');
      const key = `new-${type}-${Date.now()}`;
      let nodeData = {};
      if (type === 'entity') {
        nodeData = { category: 'Entity', key, name: '新实体', loc: go.Point.stringify(position), underline: false };
      } else if (type === 'attribute') {
        nodeData = { category: 'Attribute', key, name: '新属性', loc: go.Point.stringify(position), underline: false };
      }
      myDiagram.model.addNodeData(nodeData);
      myDiagram.commitTransaction('addNode');
    };
    // 实体过滤
    const filterEntities = (entity) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('filterEntities');
      if (entity === 'all') {
        myDiagram.nodes.each(node => node.visible = true);
        myDiagram.links.each(link => link.visible = true);
      } else {
        myDiagram.nodes.each(node => {
          node.visible = node.data.key === entity || node.data.category === 'Attribute' && node.data.key.startsWith(entity + '-');
        });
        myDiagram.links.each(link => {
          link.visible = link.data.from === entity || link.data.to === entity || (link.data.from && link.data.from.startsWith(entity + '-')) || (link.data.to && link.data.to.startsWith(entity + '-'));
        });
      }
      myDiagram.commitTransaction('filterEntities');
    };
    // 关系显示
    const toggleRelations = (show) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('toggleRelations');
      // 1. 控制外键连线
      myDiagram.links.each(link => {
        if (link.data.text === '1' || link.data.text === 'N') {
          link.visible = show;
        }
      });
      // 2. 控制关联节点
      myDiagram.nodes.each(node => {
        if (node.data.category === 'Relationship') {
          node.visible = show;
        }
      });
      myDiagram.commitTransaction('toggleRelations');
    };

    const getCurrentNodeColor = () => {
      const arr = myDiagram?.model?.nodeDataArray || [];
      const node = arr.find(n => n.category === 'Entity' || n.category === 'Attribute');
      return node?.color || '#000000';
    };
    const getCurrentLineColor = () => {
      const arr = myDiagram?.model?.linkDataArray || [];
      const link = arr[0];
      return link?.color || '#000000';
    };

    const getEntityList = () => {
      if (!myDiagram) return [];
      return myDiagram.model.nodeDataArray
        .filter(n => n.category === 'Entity')
        .map(n => n.key);
    };

    const getEntityListWithRelationships = () => {
      if (!myDiagram) return [];
      return myDiagram.model.nodeDataArray
        .filter(n => n.category === 'Entity' || n.category === 'Relationship')
        .map(n => n.key);
    };

    // 设置节点下划线
    const setNodeUnderline = (key, underline) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('setNodeUnderline');
      const node = myDiagram.model.nodeDataArray.find(n => n.key === key);
      if (node) {
        if (typeof node.underline === 'undefined') {
          myDiagram.model.setDataProperty(node, 'underline', false);
        }
        myDiagram.model.setDataProperty(node, 'underline', underline);
      }
      myDiagram.commitTransaction('setNodeUnderline');
    };
    // 删除节点
    const deleteNode = (key) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('deleteNode');
      const node = myDiagram.findNodeForKey(key);
      if (node) {
        myDiagram.remove(node);
      }
      myDiagram.commitTransaction('deleteNode');
    };
    // 添加关系线
    const addRelationshipLine = (from, to, relationName) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('addRelationshipLine');
      // 创建关系节点
      const relationKey = `rel-${from}-${to}-${Date.now()}`;
      myDiagram.model.addNodeData({
        category: 'Relationship',
        key: relationKey,
        name: relationName,
        underline: false
      });
      // from -> 关系
      myDiagram.model.addLinkData({ from, to: relationKey, text: 'N' });
      // 关系 -> to
      myDiagram.model.addLinkData({ from: relationKey, to, text: '1' });
      myDiagram.commitTransaction('addRelationshipLine');
    };
    // 给当前节点添加连接
    const addConnectionToNode = (type, name, fromKey) => {
      if (!myDiagram) return;
      myDiagram.startTransaction('addConnectionToNode');
      const key = `new-${type}-${Date.now()}`;
      if (type === 'attribute') {
        myDiagram.model.addNodeData({ category: 'Attribute', key, name, underline: false });
        myDiagram.model.addLinkData({ from: fromKey, to: key });
      } else if (type === 'relationship') {
        myDiagram.model.addNodeData({ category: 'Relationship', key, name, underline: false });
        myDiagram.model.addLinkData({ from: fromKey, to: key });
      } else if (type === 'entity') {
        myDiagram.model.addNodeData({ category: 'Entity', key, name, underline: false });
        myDiagram.model.addLinkData({ from: fromKey, to: key });
      }
      myDiagram.commitTransaction('addConnectionToNode');
    };

    // 更新节点名称
    const updateNodeName = (key, name) => {
      console.log(key, name)
      if (!myDiagram) return;
      myDiagram.startTransaction('updateNodeName');
      const node = myDiagram.model.nodeDataArray.find(n => n.key === key);
      if (node) {
        myDiagram.model.setDataProperty(node, 'name', name);
      }
      myDiagram.commitTransaction('updateNodeName');
    };

    const getNodeByKey = (key) => {
      if (!myDiagram) return null;
      return myDiagram.model.nodeDataArray.find(n => n.key === key) || null;
    };

    // expose
    return {
      diagramDiv,
      updateDiagram,
      openExportDialog,
      isExportDialogVisible,
      handleExportConfirm,
      // 新增
      showSaveDialog,
      saveForm,
      saveType,
      handleSave,
      handleSaveConfirm,
      handleSaveCancel,
      showAttributes,
      toggleAttributes,
      exportOptions,
      handleExportMenu,
      // 新增暴露
      setNodeColor,
      setLineColor,
      setLayout,
      setLinkLabelPosition,
      addNode,
      filterEntities,
      toggleRelations,
      getCurrentNodeColor,
      getCurrentLineColor,
      undo,
      redo,
      fullscreen,
      getEntityList,
      getEntityListWithRelationships,
      setNodeUnderline,
      deleteNode,
      addRelationshipLine,
      addConnectionToNode,
      updateNodeName,
      getNodeByKey
    };
  }
};
</script>

<style lang="scss" scoped>
.diagram-output {
  height: calc(100vh - 140px);
  transition: all 0.3s ease;
  border: none;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);

  &:hover {
    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
  }

  :deep(.el-card__header) {
    padding: 16px 20px;
    border-bottom: 1px solid #ebeef5;
    background: #fff;
  }

  :deep(.el-card__body) {
    height: calc(100% - 60px);
    padding: 0;
    position: relative;
  }
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;

  .header-left {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 16px;
    font-weight: 600;
    color: #303133;

    i {
      font-size: 18px;
      color: #67c23a;
    }
  }
}

.diagram-wrapper {
  height: 100%;
  padding: 16px;
  background-color: #f8f9fa;
}

.diagram-container {
  width: 100%;
  height: 100%;
  background-color: white;
  position: relative;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  transition: all 0.3s ease;

  &:hover {
    border-color: #c0c4cc;
  }

  :deep(canvas) {
    outline: none;
  }
}
</style>
